package org.bangumibuddy.websocket;

import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.websocket.*;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.bangumibuddy.mapper.TorrentsMapper;
import org.bangumibuddy.pojo.DownloadInfo;
import org.bangumibuddy.pojo.RssDownloadInfo;
import org.bangumibuddy.service.DownloadService;
import org.bangumibuddy.service.RssService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.concurrent.*;

@ServerEndpoint("/socket/downloadInfo/{animeId}")
@Component
@Slf4j
public class DownloadInfoWebSocket {

    static RssService rssService;

    static DownloadService downloadService;

    private static ConcurrentHashMap<Long, DownloadInfoWebSocket> webSocketMap = new ConcurrentHashMap<>();

    private ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
    private ScheduledFuture<?> scheduledFuture;

    private Session session;

    private Long animeId;

    private static TorrentsMapper torrentsMapper;

    public DownloadInfoWebSocket() {
    }

    @Autowired
    public void setDownloadService(RssService rssService) {
        DownloadInfoWebSocket.rssService = rssService;
    }

    @Autowired
    public void setDownloadService(DownloadService downloadService) {
        DownloadInfoWebSocket.downloadService = downloadService;
    }

    @OnOpen
    public void onOpen(Session session, @PathParam("animeId") Long animeId) {
        log.info("DownloadInfoSocket opened: " + animeId);
        webSocketMap.put(animeId, this);
        this.session = session;
        this.animeId = animeId;

        this.scheduledFuture = this.scheduler.scheduleAtFixedRate(this::GetDownloadInfoScheduled, 0, 1, TimeUnit.SECONDS);
    }

    @SneakyThrows
    private void GetDownloadInfoScheduled() {
        if(!session.isOpen())
            this.onClose();

        log.info("Get download info for anime: " + animeId);
        List<RssDownloadInfo> rssDownloadInfos = rssService.getRssDownloadInfoByAnimeId(animeId);
        if (rssDownloadInfos.size() == 0)
            return;

        for (RssDownloadInfo rssDownloadInfo : rssDownloadInfos) {
            List<DownloadInfo> downloadInfos = rssDownloadInfo.getDownloadInfos();
            List<String> hashes = downloadInfos.stream().map(DownloadInfo::getHash).toList();
            List<DownloadInfo> downloadInfoByHashes = downloadService.getDownloadInfoByHashes(hashes);
            rssDownloadInfo.setDownloadInfos(downloadInfoByHashes);
        }

        ObjectMapper objectMapper = new ObjectMapper();
        try {
            String infojson = objectMapper.writeValueAsString(rssDownloadInfos);
            session.getBasicRemote().sendText(infojson);
        } catch (Exception e) {
            log.error("Send message error: " + e.getMessage());
            this.onClose();
        }
    }

    @OnMessage
    public void onMessage(String message) {
        // do nothing
    }

    @OnClose
    @SneakyThrows
    public void onClose() {
        log.info("DownloadInfoSocket closed: " + animeId);
        this.scheduledFuture.cancel(true);
        session.close();
        webSocketMap.remove(animeId);
    }

    @OnError
    public void onError(Throwable error) {
        log.error("DownloadInfoSocket error: " + error.getMessage());
    }

}
